///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include <Scene/ObjectLoaders/MS3DObjectLoader.h>
#include <File/File.h>
#include <Material/Material.h>
#include <Texture/TextureManager.h>
//#include <Shader/ShaderManager.h>
#include <Model/MeshManager.h>
#include <Model/VertexBuffer.h>
#include <Model/Mesh.h>
#include <Material/Material.h>
#include <Material/MaterialManager.h>
#include <string.h>
#include <Scene/Object.h>

//-----------------------------------------------------------------------------

// byte-align structures
#ifdef _MSC_VER
#	pragma pack( push, packing )
#	pragma pack( 1 )
#	define PACK_STRUCT
#elif defined( __GNUC__ )
#	define PACK_STRUCT	__attribute__((packed))
#else
#	error you must byte-align these structures with the appropriate compiler directives
#endif

// File header
struct MS3DHeader
{
	char m_ID[10];
	int m_version;
} PACK_STRUCT;

// Vertex information
struct MS3DVertex
{
	u8 m_flags;
	float m_vertex[3];
	char m_boneID;
	u8 m_refCount;
} PACK_STRUCT;

// Triangle information
struct MS3DTriangle
{
	u16 m_flags;
	u16 m_vertexIndices[3];
	float m_vertexNormals[3][3];
	float m_s[3], m_t[3];
	u8 m_smoothingGroup;
	u8 m_groupIndex;
} PACK_STRUCT;

// Material information
struct MS3DMaterial
{
    char m_name[32];
    float m_ambient[4];
    float m_diffuse[4];
    float m_specular[4];
    float m_emissive[4];
    float m_shininess;	// 0.0f - 128.0f
    float m_transparency;	// 0.0f - 1.0f
    u8 m_mode;	// 0, 1, 2 is unused now
    char m_texture[128];
    char m_alphamap[128];
} PACK_STRUCT;

//	Joint information
struct MS3DJoint
{
	u8 m_flags;
	char m_name[32];
	char m_parentName[32];
	float m_rotation[3];
	float m_translation[3];
	u16 m_numRotationKeyframes;
	u16 m_numTranslationKeyframes;
} PACK_STRUCT;

// Keyframe data
struct MS3DKeyframe
{
	float m_time;
	float m_parameter[3];
} PACK_STRUCT;

// Default alignment
#ifdef _MSC_VER
#	pragma pack( pop, packing )
#endif

#undef PACK_STRUCT


//-----------------------------------------------------------------------------

MS3DObjectLoader::MS3DObjectLoader()
{
	RegisterLoader( this );
}

//-----------------------------------------------------------------------------

MS3DObjectLoader::~MS3DObjectLoader()
{
	UnregisterLoader( this );
}

//-----------------------------------------------------------------------------

bool	MS3DObjectLoader::CanLoad(File* file)
{
	if( !file->IsOpen() )
	{
		return false;
	}

	file->Seek( 0 );

	//check for extension...

	MS3DHeader Header;
	file->Read( &Header, sizeof( MS3DHeader ), 1 );

	if ( strncmp( Header.m_ID, "MS3D000000", 10 ) != 0 )
		return false; // "Not a valid Milkshape3D model file."

	if ( Header.m_version < 3 || Header.m_version > 4 )
		return false; // "Unhandled file version. Only Milkshape3D Version 1.3 and 1.4 is supported." );

	return true;
}

//-----------------------------------------------------------------------------

bool	MS3DObjectLoader::Load(File* file)
{
	if( !CanLoad( file ) )
	{
		return false;
	}

	file->Seek( 0 );

	file->Seek( sizeof( MS3DHeader ) );

	u16 nVertices; 
	file->Read( &nVertices, sizeof( u16 ), 1 );

	MS3DVertex *pVertex = MGL_NEW MS3DVertex[nVertices];
	file->Read( pVertex, sizeof( MS3DVertex ), nVertices );

	u16 nTriangles;
	file->Read( &nTriangles, sizeof( u16 ), 1 );

	MS3DTriangle *pTriangle = MGL_NEW MS3DTriangle[nTriangles];
	file->Read( pTriangle, sizeof( MS3DTriangle ), nTriangles );

	u16 nGroups;
	file->Read( &nGroups, sizeof( u16 ), 1 );

	u8 flags;
	s8 name[32];

	s8	matIndices;

	for ( int i = 0; i < nGroups; i++ )
	{
		file->Read( &flags, sizeof( u8 ), 1 );
		file->Read( name, sizeof( u8 ), 32 );

		u16 nMeshTraingles;
		file->Read( &nMeshTraingles , sizeof( u16 ), 1 );

		VertexBuffer* vbp = MGL_NEW VertexBuffer();
		vbp->AddComponent( eVDT_POSITION, eVCT_FLOAT, sizeof( f32 ), 3 );
		vbp->AddComponent( eVDT_NORMAL, eVCT_FLOAT, sizeof( f32 ), 3 );
		vbp->AddComponent( eVDT_TEXCOORD, eVCT_FLOAT, sizeof( f32 ), 2 );
		vbp->InitData( nMeshTraingles * 3 );
		f32* vbpData = (f32*) vbp->GetDataPtr();

		u16	triIdx;
		u16 vIdx;
		for( int t = 0; t < nMeshTraingles; t++ )
		{
			file->Read( &triIdx, sizeof( u16 ), 1 );

			for( int j = 0; j < 3; j++ )
			{
				vIdx = pTriangle[ triIdx ].m_vertexIndices[j];
				memcpy( vbpData, pVertex[ vIdx ].m_vertex, sizeof( f32 ) * 3 );
				vbpData += 3;

				memcpy( vbpData, pTriangle[ triIdx ].m_vertexNormals[j], sizeof( f32 ) * 3 );
				vbpData += 3;

				*vbpData = pTriangle[ triIdx ].m_s[j];
				vbpData++;

				*vbpData = pTriangle[ triIdx ].m_t[j];
				vbpData++;
			}
		}

		file->Read( &matIndices, sizeof( s8 ), 1 );

		MeshBuffers mb;
		mb.indexBuffer = NULL;
		mb.vertexBuffer = vbp;

		MeshData *mesh = MGL_NEW MeshData( file->GetFilename() );
		mesh->AddMeshBuffers( mb );
		//mesh->generateBoundingSphere();
	}

	u16 nMaterials;
	file->Read( &nMaterials, sizeof( u16 ), 1 );
	MS3DMaterial pMaterial;
	Material *mat = NULL;
	for ( int i = 0; i < nMaterials; i++ )
	{
		file->Read( &pMaterial, sizeof( MS3DMaterial ), 1 );
		mat = MGL_NEW Material( file->GetFilename() );

		memcpy( mat->m_ambient, pMaterial.m_ambient, sizeof( float )*4 );
		memcpy( mat->m_diffuse, pMaterial.m_diffuse, sizeof( float )*4 );
		memcpy( mat->m_specular, pMaterial.m_specular, sizeof( float )*4 );
		memcpy( mat->m_emissive, pMaterial.m_emissive, sizeof( float )*4 );
		mat->m_shininess = pMaterial.m_shininess;
		//std::string texfilename = std::string( pMaterial.m_texture );
		//mat->m_texture = TextureManager::GetInstance()->GetTexture( texfilename );
	}

	SAFE_DEL_ARRAY( pVertex );
	SAFE_DEL_ARRAY( pTriangle );

	return true;
}

//-----------------------------------------------------------------------------

Object*	MS3DObjectLoader::LoadObject(File* file)
{
	if( !Load( file ) )
	{
		return NULL;
	}

	Object* obj = MGL_NEW Object();
	ASSERT( obj );
	if( MeshManager::GetInstance()->IsMeshLoaded( file->GetFilename() ) )
	{
		MeshData* meshData = MeshManager::GetInstance()->GetMesh( file->GetFilename() );

		if( MaterialManager::GetInstance()->HaveMaterial( file->GetFilename() ) )
		{
			Material* mat = MaterialManager::GetInstance()->GetMaterial( file->GetFilename() );
			Mesh* mesh = MGL_NEW Mesh( file->GetFilename() );
			mesh->SetMeshData( meshData );
			mesh->SetMaterial( mat );
			obj->AddMesh( mesh );
		}
	}
	else
	{
		DEBUG_ERR( "[OBJObjectLoader::LoadObject] Mesh not loaded '%s'!\n", file->GetFilename().c_str() );
	}

	return obj;
}

//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
